from numpy import *
#####################################################################################
# Class
#####################################################################################
class GenericNetwork():
    input = None
    output = None
    hidden = None
    links = None

    def set_hidden(self, layer):
        if not self.hidden:
            self.hidden = []
        self.hidden.append(layer)
        return layer

    def set_link(self,link):
        if not self.links:
            self.links = []
        self.links.append(link)
        return link

    def propogate_one_step(self,pattern):
        for number, item in enumerate(pattern):
            self.input.data[number] = item

        for link in self.links:
            link.propogate()

        return self.output.activated_data()

    def str_layers(self):
        try:
            out = str(self.input.size) + ' > ['
            out += '|'.join([str(layer.size) for layer in self.hidden])
            out += '] > ' + str(self.output.size)
        except:
            out = 'None'
        return out

    def str_links(self):
        try:
            out = ' > '.join([str(link.shape) for link in self.links])
        except:
            out = 'None'
        return out

    def str_layers_full(self):
        print self.input
        for hidden in self.hidden:
            print hidden
        print self.output
        print 'links'
        for l in self.links:
            print l.data

    def __str__(self):
        out = '* generic network'
        out += '\nlayers: '
        out += self.str_layers()
        out += '\nlinks: '
        out += self.str_links()
        return out

#####################################################################################
# Class
#####################################################################################
class GenericLayer():
    def __init__(self, rows, activation=None):
        self.data = mat(empty(rows)).transpose()
        self.size = self.data.size
        self.activation = activation

    def activated_data(self):
        if self.activation:
            return self.activation(self.data)
        return self.data

    def __str__(self):
        return str(self.data)

#####################################################################################
# Class
#####################################################################################
class GenericLink():
    def __init__(self, source, destination, spread = 0, center = 0):
        self.data = random.random((source.size,destination.size)) * spread - spread/2 +center
        self.data = mat(self.data).transpose()
        self.source = source
        self.destination = destination
        self.shape = self.data.shape

    def propogate(self):
        data = self.source.activated_data()
        link = self.data
        self.destination.data = link * data

    def __str__(self):
        return 'GenericLink'

#####################################################################################
# Class
#####################################################################################
class GenericTrainer():
    def __init__(self, network, dataset):
        self.network = network
        self.dataset = dataset

    def train_one_step(self,pattern):
        output = self.network.propogate_one_step(pattern[0])
        desired = array(pattern[1], dtype=float32)
        error = desired - output
        print 'output: ' + str(output)
        print 'desired output: ' + str(desired)
        print 'error: ' + str(error)

    def train(self, steps = 0):
        counter = 0
        pattern = self.dataset.fetch_pattern()
        while pattern and (not steps or counter<steps):
            counter+=1
            print 'training step #' + str(counter)
            self.train_one_step(pattern)
            pattern = self.dataset.fetch_pattern()
            print